<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">
  <meta name="google-site-verification" content="yF8HXsFuCTrePDFRnSkN2kkzV2ypBjtywzXsfVXOOV8">
  <meta name="baidu-site-verification" content="VxrVAiQAZ9">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"yang0033.gitee.io","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":true,"preload":true},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="使用 Run Loop 对象一个 run loop 对象提供了一些主要接口用于向你的 run loop 中添加 input source ，timers， 和run loop observer，并且运行它。每一条线程有且只有一个run loop 与他相关联。在 Cocoa 中，这个对象是 NSRunLoop 类的一个实例。在底层的应用中，它是指向 CFRunLoopRef 这种不透明类型的一个指针">
<meta property="og:type" content="article">
<meta property="og:title" content="配置一个基于_port_的_input_source">
<meta property="og:url" content="http://yang0033.gitee.io/2016/04/08/%E9%85%8D%E7%BD%AE%E4%B8%80%E4%B8%AA%E5%9F%BA%E4%BA%8E_port_%E7%9A%84_input_source/index.html">
<meta property="og:site_name" content="SuperYang&#96;s Blog">
<meta property="og:description" content="使用 Run Loop 对象一个 run loop 对象提供了一些主要接口用于向你的 run loop 中添加 input source ，timers， 和run loop observer，并且运行它。每一条线程有且只有一个run loop 与他相关联。在 Cocoa 中，这个对象是 NSRunLoop 类的一个实例。在底层的应用中，它是指向 CFRunLoopRef 这种不透明类型的一个指针">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/Art/custominputsource.jpg">
<meta property="article:published_time" content="2016-04-08T02:38:48.000Z">
<meta property="article:modified_time" content="2020-07-09T08:06:21.460Z">
<meta property="article:author" content="SuperYang">
<meta property="article:tag" content="RunLoop">
<meta property="article:tag" content="Thread">
<meta property="article:tag" content="RunLoopSource">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/Art/custominputsource.jpg">

<link rel="canonical" href="http://yang0033.gitee.io/2016/04/08/%E9%85%8D%E7%BD%AE%E4%B8%80%E4%B8%AA%E5%9F%BA%E4%BA%8E_port_%E7%9A%84_input_source/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>配置一个基于_port_的_input_source | SuperYang`s Blog</title>
  
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-71812998-3"></script>
    <script>
      if (CONFIG.hostname === location.hostname) {
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'UA-71812998-3');
      }
    </script>


  <script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?8ff3295288514de6f89f314c23ee40ab";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">SuperYang`s Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">落魄程序员在线炒饭</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
        <li class="menu-item menu-item-message">

    <a href="/message/" rel="section"><i class="fa fa-music fa-fw"></i>留言板 | Music</a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yang0033.gitee.io/2016/04/08/%E9%85%8D%E7%BD%AE%E4%B8%80%E4%B8%AA%E5%9F%BA%E4%BA%8E_port_%E7%9A%84_input_source/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://tva2.sinaimg.cn/crop.259.17.290.290.180/c2abdfa9jw8ez7appr3p2j20g40a2t91.jpg?KID=imgbed,tva&Expires=1594186820&ssig=BdxhDdi1Ti">
      <meta itemprop="name" content="SuperYang">
      <meta itemprop="description" content="牛肉炒饭不要香菜，老板收钱">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="SuperYang`s Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          配置一个基于_port_的_input_source
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2016-04-08 10:38:48" itemprop="dateCreated datePublished" datetime="2016-04-08T10:38:48+08:00">2016-04-08</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-07-09 16:06:21" itemprop="dateModified" datetime="2020-07-09T16:06:21+08:00">2020-07-09</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
            </span>

          
            <span id="/2016/04/08/%E9%85%8D%E7%BD%AE%E4%B8%80%E4%B8%AA%E5%9F%BA%E4%BA%8E_port_%E7%9A%84_input_source/" class="post-meta-item leancloud_visitors" data-flag-title="配置一个基于_port_的_input_source" title="阅读次数">
              <span class="post-meta-item-icon">
                <i class="fa fa-eye"></i>
              </span>
              <span class="post-meta-item-text">阅读次数：</span>
              <span class="leancloud-visitors-count"></span>
            </span>
  
  <span class="post-meta-item">
    
      <span class="post-meta-item-icon">
        <i class="far fa-comment"></i>
      </span>
      <span class="post-meta-item-text">Valine：</span>
    
    <a title="valine" href="/2016/04/08/%E9%85%8D%E7%BD%AE%E4%B8%80%E4%B8%AA%E5%9F%BA%E4%BA%8E_port_%E7%9A%84_input_source/#valine-comments" itemprop="discussionUrl">
      <span class="post-comments-count valine-comment-count" data-xid="/2016/04/08/%E9%85%8D%E7%BD%AE%E4%B8%80%E4%B8%AA%E5%9F%BA%E4%BA%8E_port_%E7%9A%84_input_source/" itemprop="commentCount"></span>
    </a>
  </span>
  
  <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="far fa-file-word"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>13k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="far fa-clock"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>12 分钟</span>
            </span>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h2 id="使用-Run-Loop-对象"><a href="#使用-Run-Loop-对象" class="headerlink" title="使用 Run Loop 对象"></a>使用 Run Loop 对象</h2><p>一个 run loop 对象提供了一些主要接口用于向你的 run loop 中添加 input source ，timers， 和run loop observer，并且运行它。每一条线程有且只有一个run loop 与他相关联。在 Cocoa 中，这个对象是 <a href="https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSRunLoop_Class/index.html#//apple_ref/occ/cl/NSRunLoop" target="_blank" rel="noopener">NSRunLoop</a> 类的一个实例。在底层的应用中，它是指向 <a href="https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFRunLoopRef/index.html#//apple_ref/c/tdef/CFRunLoopRef" target="_blank" rel="noopener">CFRunLoopRef</a> 这种不透明类型的一个指针。 </p>
<h2 id="获取-Run-Loop-对象"><a href="#获取-Run-Loop-对象" class="headerlink" title="获取 Run Loop 对象"></a>获取 Run Loop 对象</h2><p>你需要使用以下其中之一来获取当前线程的 Run Loop ：</p>
<a id="more"></a>

<ul>
<li>在 Cocoa 中，使用 <a href="https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSRunLoop_Class/index.html#//apple_ref/occ/cl/NSRunLoop" target="_blank" rel="noopener">NSRunLoop</a> 的类方法 <a href="https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSRunLoop_Class/index.html#//apple_ref/occ/clm/NSRunLoop/currentRunLoop" target="_blank" rel="noopener">currentRunLoop</a> 去拿到一个 <code>NSRunLoop</code> 对象。</li>
<li>使用 <a href="https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFRunLoopRef/index.html#//apple_ref/c/func/CFRunLoopGetCurrent" target="_blank" rel="noopener">CFRunLoopGetCurrent</a> 函数。</li>
</ul>
<p>尽管这两种方法不是 <a href="https://developer.apple.com/library/mac/documentation/General/Conceptual/CocoaEncyclopedia/Toll-FreeBridgin/Toll-FreeBridgin.html" target="_blank" rel="noopener">toll-free bridged type</a>(在Foundation 和 Core Foundation 中拥有等价替换接口的能力的类型)的类型,但是如果你需要可以从 <code>NSRunLoop</code> 对象里拿到 CFRunLoopRef 这种<code>不透明类型</code>(苹果封装在内部的C语言类型)。<code>NSRunLoop</code> 类定义了 <code>getCFRunLoop</code> 方法用来返回一个可以传入到 Core Foundation 代码中的 <code>CFRunLoopRef</code> 类型的C语言指针对象（结构体指针）。这两种对象都可以来自于同一个 run loop，你可以根据你的需要来选择具体使用 <code>NSRunLoop</code> 和 <code>CFRunLoopRef</code> 这两种对象的哪一种。</p>
<h2 id="配置-Run-Loop"><a href="#配置-Run-Loop" class="headerlink" title="配置 Run Loop"></a>配置 Run Loop</h2><p>在你运行一个子线程的 run loop 之前，你必须向其添加至少一个 input source 或者 timer。如果 run loop 没有任何需要监视的 source， 它将会在你尝试运行它的时候立即退出。请参考<a href="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW7" target="_blank" rel="noopener">配置RunLoop Sounce</a>（本文接下来的章节将有介绍）。</p>
<p>除了安装 source，你还可以 run loop observer 并且使用他们检测 runloop的处于不同执行阶段。为了安装 run loop observer ，你需要创建一个 CFRunLoopObserverRef 不透明类型的指针并使用 <a href="https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFRunLoopRef/index.html#//apple_ref/c/func/CFRunLoopAddObserver" target="_blank" rel="noopener">CFRunLoopAddObserver</a> 函数将 Observer 添加到你的 run loop 中去，Run Loop Observer 必须使用 Core Foundation 框架接口创建，在 Cocoa 应用中也一样。</p>
<p>表 3-1 展示了在线程 runloop 中，添加 run loop Observer 的主要代码流程。本例的目的旨在告诉你如何创建一个 run loop Observer， 所以代码只是简单设置了一个run loop Observer 用来监视 run loop 的所有活动 。基本的处理代码（没有展示）仅仅是日志输出 run loop 的各项活动行为 作为 timer 的事件回调。</p>
<p>表3-1 创建 runloop Observer</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)threadMain &#123;</span><br><span class="line">    <span class="comment">// 应用使用垃圾回收，所以不需要 自动释放池 autorelease pool</span></span><br><span class="line">    <span class="built_in">NSRunLoop</span> *myRunLoop = [<span class="built_in">NSRunLoop</span> currentRunLoop];</span><br><span class="line">    <span class="comment">// 创建一个 run loop observer 并且将他添加到当前 run loop 中去</span></span><br><span class="line">    <span class="comment">/*!</span></span><br><span class="line"><span class="comment">     *  @author 杨超, 16-01-13 15:01:45</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     *  @brief CFRunLoopObserverContext 用来配置 CFRunLoopObserver 对象行为的结构体</span></span><br><span class="line"><span class="comment">     typedef struct &#123;</span></span><br><span class="line"><span class="comment">        CFIndex	version;</span></span><br><span class="line"><span class="comment">        void *	info;</span></span><br><span class="line"><span class="comment">        const void *(*retain)(const void *info);</span></span><br><span class="line"><span class="comment">        void	(*release)(const void *info);</span></span><br><span class="line"><span class="comment">        CFStringRef	(*copyDescription)(const void *info);</span></span><br><span class="line"><span class="comment">     &#125; CFRunLoopObserverContext;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     *  @param version 结构体版本号，必须为0</span></span><br><span class="line"><span class="comment">     *  @param info 一个程序预定义的任意指针，可以再 run loop Observer 创建时为其关联。这个指针将被传到所有 context 多定义的所有回调中。</span></span><br><span class="line"><span class="comment">     *  @param retain 程序定义 info 指针的内存保留（retain）回调,可以为 NULL</span></span><br><span class="line"><span class="comment">     *  @param release 程序定义 info 指针的内存释放（release）回调，可以为 NULL</span></span><br><span class="line"><span class="comment">     *  @param copyDescription 程序定于 info 指针的 copy 描述回调，可以为 NULL</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     *  @since</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="built_in">CFRunLoopObserverContext</span> context = &#123;<span class="number">0</span> , (__bridge <span class="keyword">void</span> *)(<span class="keyword">self</span>), <span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>&#125;;</span><br><span class="line">    <span class="built_in">CFRunLoopObserverRef</span> observer = <span class="built_in">CFRunLoopObserverCreate</span>(kCFAllocatorDefault, kCFRunLoopAllActivities, <span class="literal">YES</span>, <span class="number">0</span>, &amp;myRunLoopObserverCallBack, &amp;context);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (observer) &#123;</span><br><span class="line">        <span class="built_in">CFRunLoopRef</span> cfLoop = [myRunLoop getCFRunLoop];</span><br><span class="line">        <span class="built_in">CFRunLoopAddObserver</span>(cfLoop, observer, kCFRunLoopDefaultMode);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 创建并安排好 timer</span></span><br><span class="line">    [<span class="built_in">NSTimer</span> scheduledTimerWithTimeInterval:<span class="number">0.1</span> target:<span class="keyword">self</span> selector:<span class="keyword">@selector</span>(doFireTimer) userInfo:<span class="literal">nil</span> repeats:<span class="literal">YES</span>];</span><br><span class="line">    <span class="built_in">NSInteger</span> loopCount = <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        <span class="comment">// 3秒后运行 run loop 实际效果是每三秒进入一次当前 while 循环</span></span><br><span class="line">        [myRunLoop runUntilDate:[<span class="built_in">NSDate</span> dateWithTimeIntervalSinceNow:<span class="number">3</span>]];</span><br><span class="line">        loopCount --;</span><br><span class="line">    &#125; <span class="keyword">while</span> (loopCount);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> myRunLoopObserverCallBack(<span class="built_in">CFRunLoopObserverRef</span> observer, <span class="built_in">CFRunLoopActivity</span> activity, <span class="keyword">void</span> *info) &#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"observer正在回调\n%@----%tu----%@"</span>, observer, activity, info);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)doFireTimer &#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"计时器回调"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当为一个长期存活的现场配置 runloop 时，至少添加一个 input source 去接收消息。尽管你可以仅仅使用一个 关联的timer 就可以进入 run loop，一旦 timer 启动，通常都会被作废掉，这将会硬气 run loop 的退出。关联一个重复执行的 timer 定时器可以保持让 runloop 在很长的一段时期内得以运行，但是需要周期性的去启动定时器 timer 来唤醒你的线程，这是投票有效的另一种形式（这句莫名其妙，不懂是干吗的）。相比之下， input source 会等待事件的发生，并保持线程处于睡眠状态直到事件确实发生了。</p>
<h2 id="开动-run-loop"><a href="#开动-run-loop" class="headerlink" title="开动 run loop"></a>开动 run loop</h2><p>在应用中，只有在子线程中才是有必要开启 run loop 的，一个 run loop 必须至少有一个用来监视的 input source 。如果一个关联的都没有，run loop 将会立即退出。</p>
<p>下面有一些方法开启 run loop：</p>
<ul>
<li>无条件的</li>
<li>通过一套时间限制</li>
<li>在一个特别的 mode 下</li>
</ul>
<p>无条件的进入你的 run loop 是最简单的选项，但这种也是最不可取的。无条件地运行你的 run loop 将会使你的线程进入进入永久的循环中，这使你很难控制运行循环本身。你可以添加和移除 input source 和 timer，但是只有一种方式去停止 run loop，那就是将它杀死。同时也不存在在自定义 mode 中运行 run loop 的方法。</p>
<p>为了替代无条件的运行 run loop ，更好的办法是使用超时值来运行 runloop。当你使用超时值时，run loop 会一直运行直到在事件来临时 或者 分配的时间结束时。当你的事件到达时，系统会分配一个 handler 去处理它，并且之后 run loop 会退出。你可以用代码重启你的 run loop 以便处理下一个事件。如果不想继续使用刚才分配时间结束的原则，也可以简单的重启 runloop 或者使用这些时间去做任何你需要做的事。</p>
<p>除了使用超时值，你也可以使用指定的 mode 运行 run loop。mode 和超时值不会互相排斥，并且都可以用来启动一个线程。</p>
<p>表 3-2 展示了一个线程入口的常用的例行程序。示例代码的关键部分展示了一个 run loop 的基础架构。本质上，你将 input sources 和 timers 添加到你的 runloop 中，然后重复的调用其中一个例行程序来启动 run loop 。每一次例行程序返回时，你需要检查一下是否满足可能会退出线程的条件。示例使用了 Core Foundation 的框架的例行程序以便检查返回结果并且可以决定如何退出 runloop。如果你是用的是 Cocoa ，你也可以使用类似的方式通过 NSRunLoop 的方法去运行 runloop ，    并且不需要检查返回值。（使用 NSRunLoop 的方法的例子可以参考 表3-14.）</p>
<p>表 3-2 运行 runloop</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)skeletionThreadMain &#123;</span><br><span class="line">    <span class="comment">// 如果你的应用没有使用垃圾回收 请在这里添加 自动释放池（ps：这示例代码也太老了，谁还用垃圾回收啊）</span></span><br><span class="line">    </span><br><span class="line">    <span class="built_in">BOOL</span> done = <span class="literal">NO</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 给 runloop 添加 source 或timer，然后做一些其他的配置</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        <span class="comment">// 开启 runloop 并且被一个 source 被处理后要返回</span></span><br><span class="line">        <span class="comment">/** SInt32 32位有符号整数 */</span></span><br><span class="line">        SInt32 result = <span class="built_in">CFRunLoopRunInMode</span>(kCFRunLoopDefaultMode, <span class="number">10</span>, <span class="literal">YES</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 如果 source 已经显式的停止了 runloop ，或者根本不存在任何 source 或 timer，将会退出。</span></span><br><span class="line">        <span class="keyword">if</span> ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished)) &#123;</span><br><span class="line">            done = <span class="literal">YES</span>;</span><br><span class="line">            <span class="comment">// 在这里检查任何其他符合退出的条件并且按需设置 done 变量的值。</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">while</span> (!done);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 在这里清除代码。确保释放任何之前创建的自动释放池。</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>可以递归开启 runloop，换句话说，你可以使用 input source 或者 timer 的例行程序来调用 <a href="https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFRunLoopRef/index.html#//apple_ref/c/func/CFRunLoopRun" target="_blank" rel="noopener">CFRunLoopRun</a>,<a href="https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFRunLoopRef/index.html#//apple_ref/c/func/CFRunLoopRunInMode" target="_blank" rel="noopener">CFRunLoopRunInMode</a>或者任何 NSRunLoop 的 runloop 启动方法。这样做你可以使用任何你想用的 mode 来运行一个 嵌套的 run loop ，包括 通过外层 run loop 使用的 mode 。</p>
<h3 id="退出-RunLoop"><a href="#退出-RunLoop" class="headerlink" title="退出 RunLoop"></a>退出 RunLoop</h3><p>有两种途径可以让 runloop 在处理事件之前退出：</p>
<ul>
<li>使用超时值配置 runloop 运行。</li>
<li>直接告诉 runloop 停止（ps：。。。这条太搞了）。</li>
</ul>
<p>使用超时值无疑是更偏爱的方法，如果你能管理它，指定一个超时值使 runloop 结束所有他的正常处理的任务， 包括在退出前向 runloop observer 发送通知。</p>
<p>使用 <a href="https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFRunLoopRef/index.html#//apple_ref/c/func/CFRunLoopStop" target="_blank" rel="noopener">CFRunLoopStop</a> 函数显示地停止 runloop，产生的结果和超时相似。runloop 会发送任何 runloop 提醒通知然后才退出。不同的是你可以将这项技术应用在你用无条件方式开启的 runloop 上。</p>
<p>尽管移除一个 runloop 的 input source 和 timer 可以造成 runloop 的退出，但这并不是一个可靠的方式来停止 runloop 。一些系统例行程序给 runloop 添加一些 input source 来处理必要的事件。你的代码可能无法看出这些 input source，你可能不能移除这些用来防止 runloop  退出的 source。</p>
<h3 id="线程安全-和-Run-Loop-对象"><a href="#线程安全-和-Run-Loop-对象" class="headerlink" title="线程安全 和 Run Loop 对象"></a>线程安全 和 Run Loop 对象</h3><p>线程安全大多取决于你用来操作 runloop 的API。Core Foundation 函数 一般来说都是线程安全的，所以可以被任何线程调用。假如你正在执行一个修改 runloop 配置的操作，那么继续吧，对拥有 runloop 的线程来说这样做仍然是很好的作法。</p>
<p>Cocoa 的 <code>NSRunLoop</code> 类内部不像 Core Foundation 中的接口那样是线程安全的。如果你要使用 NSRunLoop 类去修改你的 runloop，你只能在 runloop 所在的线程中这样做。先其他线程中的 runloop 中添加 input source 或 timer 会引起你的程序崩溃或出现不可预知的异常。</p>
<h3 id="配置-run-loop-source"><a href="#配置-run-loop-source" class="headerlink" title="配置 run loop source"></a>配置 run loop source</h3><p>接下来的章节将展示如何在 Cocoa 和 Core Foundation 中设置不同类型的 input source。</p>
<h4 id="定义一个自定义自定义-input-source"><a href="#定义一个自定义自定义-input-source" class="headerlink" title="定义一个自定义自定义 input source"></a>定义一个自定义自定义 input source</h4><p>创建一个自定义的 input source 你需要实现以下这些条件：</p>
<ul>
<li>你想要你的 source 处理的信息</li>
<li>一段调度模块的例行程序让感兴趣的客户机了解如何连接你的 input source。</li>
<li>一段处理模块例行程序用来处理任何客户机发送的请求</li>
<li>一段取消模块的例行程序用来销毁你的 source</li>
</ul>
<p>因为你创建了一个自定义的 input source 来处理自定义的信息，所以实际上的配置会设计的非常灵活。调度模块，处理模块和取消模块的例行程序几乎都是你的自定义 input source 的关键例行程序。剩下的大多数 input source 行为都发生在这些例行处理程序之外。比如，由你来定义一个工具用来将数据传到你的 input source并且传递你的 input source 的数据到其他线程中去。</p>
<p>插图 3-2 展示了一个简单的自定义 input source 的配置。在本例中，应用程序主线程维持引用了input source ， input source 的缓冲模块，还有安装 input source 的 runloop。当主线程有一个任务向切换到工作子线程中去，他会发送一个命令，命令缓冲区以及启动任务所需的任何线程的信息（因为主线程和工作子线程的 input source 都有权限去访问命令缓冲区，访问必须同步）一旦命令发送了，主线程会发送信号给 input source 来唤醒工作子线程的 runloop。一旦受到唤醒的命令， runloop 会调用 input source 的处理程序 去处理命令缓存器中缓存的命令。</p>
<p>图 3-2 操作一个自定义 input source</p>
<p><img src="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/Art/custominputsource.jpg" alt="图 3-2"></p>
<p>接下来的章节将会解释如何通过上图实现一个自定义 input source 并展示你需要实现的关键代码。</p>
<h3 id="定义-input-source"><a href="#定义-input-source" class="headerlink" title="定义 input source"></a>定义 input source</h3><p>定义一个自定义 input source 需要使用 Core Foundation 的例行程序配置你的 runloop input source 并且 将它与你的 runloop 关联。尽管基础处理程序是基于 C-语言 函数的，但这不会阻止你使用 Objective-C 或者 C++ 去封装它为面向对象的代码。</p>
<p>插图3-2中介绍的 input source 使用一个 objective-C 对象去管理一个命令缓存器，并与 runloop 进行协调。列表3-3 展示了这个对象的定义。<code>RunLoopSource</code> 对象管理一个命令缓冲器，并且使用命令缓存器接受来自其他线程的消息。该表也展示了 <code>RunLoopContext</code> 对象的定义，该对象仅仅是一个容器，用来传递一个 <code>RunLoopSource</code> 对象和应用主线程的 runloop 引用。</p>
<p>表 3-3 自定义 input source 对象的定义</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">YCRunLoopSource</span> : <span class="title">NSObject</span></span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">CFRunLoopSourceRef</span> runLoopSource;</span><br><span class="line">    <span class="built_in">NSMutableArray</span> *commands;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">id</span>)init;</span><br><span class="line"><span class="comment">// 添加</span></span><br><span class="line">- (<span class="keyword">void</span>)addToCurrentRunLoop;</span><br><span class="line"><span class="comment">// 销毁</span></span><br><span class="line">- (<span class="keyword">void</span>)invalidate;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 处理方法</span></span><br><span class="line">- (<span class="keyword">void</span>)sourceFired;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用来注册需要处理的命令的客户机接口</span></span><br><span class="line">- (<span class="keyword">void</span>)addCommand:(<span class="built_in">NSInteger</span>)command withData:(<span class="keyword">id</span>)data;</span><br><span class="line">- (<span class="keyword">void</span>)fireAllCommandsOnRunLoop:(<span class="built_in">CFRunLoopSourceRef</span>)runloop;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这些是CFRunLoopRef 的回调函数</span></span><br><span class="line"><span class="comment">/** 调度函数 */</span></span><br><span class="line"><span class="keyword">void</span> RunLoopSourceScheduleRoutine(<span class="keyword">void</span> *info, <span class="built_in">CFRunLoopRef</span> r1, <span class="built_in">CFStringRef</span> mode);</span><br><span class="line"><span class="comment">/** 处理函数 */</span></span><br><span class="line"><span class="keyword">void</span> RunLoopSourcePerformRoutine (<span class="keyword">void</span> *info);</span><br><span class="line"><span class="comment">/** 取消函数 */</span></span><br><span class="line"><span class="keyword">void</span> RunLoopSourceCancelRoutine (<span class="keyword">void</span> *info, <span class="built_in">CFRunLoopRef</span> rl, <span class="built_in">CFStringRef</span> mode);</span><br><span class="line"></span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// RunLoopContext 是一个 在注册 input source 时使用的容器对象</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">YCRunLoopContext</span> : <span class="title">NSObject</span></span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">CFRunLoopRef</span> runLoop;</span><br><span class="line">    YCRunLoopSource *source;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/** 持有 runloop 和 source */</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">readonly</span>) <span class="built_in">CFRunLoopRef</span> runLoop;</span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">readonly</span>) YCRunLoopSource *source;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">id</span>)initWithSource:(YCRunLoopSource*)src andLoop:(<span class="built_in">CFRunLoopRef</span>)loop;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@end</span></span><br></pre></td></tr></table></figure>

<p>尽管 Objective-C 代码管理着 input source 的自定义数据。关联一个 input source 到一个具备 基于 C-语言 的回调函数的 runloop 。其中第一个函数是当你实际将 input source 添加到 runloop 中的时刻调用。流程将展示在 表 3-4 中。因为这个 input source 仅只有一个 客户机（主线程）。它使用调度者函数通过目标线程 application 的代理发送消息在目标线程注册自己。当 application 的代理和 input source 进行通信时 ,会使用 RunLoopContext 对象中的 <code>info</code> 信息来完成这个事。</p>
<p>表 3-4 调度 run loop source</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> RunLoopSourceScheduleRoutine(<span class="keyword">void</span> *info, <span class="built_in">CFRunLoopRef</span> r1, <span class="built_in">CFStringRef</span> mode)&#123;</span><br><span class="line">    YCRunLoopSource *obj = (__bridge YCRunLoopSource *)info;</span><br><span class="line">    <span class="comment">// 这里的 Appdelegate 是主线程的代理 	</span></span><br><span class="line">    AppDelegate *del = [AppDelegate sharedAppDelegate];</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 上下文对象中持有source自己</span></span><br><span class="line">    YCRunLoopContext *theContext = [[YCRunLoopContext alloc] initWithSource:obj andLoop:r1];</span><br><span class="line">    <span class="comment">// 通过代理去注册 Source 自己 </span></span><br><span class="line">    [del performSelectorOnMainThread:<span class="keyword">@selector</span>(registerSource:) withObject:theContext waitUntilDone:<span class="literal">NO</span>];</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>其中最重要的回调例行程序是当你的 input source 被信号激活时处理自定义数据的部分。表3-5中展示了与 <code>RunLoopSource</code> 对象关联的执行者回调例行程$序,这个函数仅仅转发用来 <code>sourceFired</code> 方法工作的请求，该请求用来处理任何 <code>command buffer</code> （命令缓冲区）中存在的命令。</p>
<p>表3-5 input source 中的执行者</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> RunLoopSourcePerformRoutine (<span class="keyword">void</span> *info)</span><br><span class="line">&#123;</span><br><span class="line">    RunLoopSource*  obj = (RunLoopSource*)info;</span><br><span class="line">    [obj sourceFired];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果你使用 <code>CFRunLoopSourceInvalidate</code> 函数将 input source 从 runloop 重移除。系统会调用你的 input source 中的取消者例行程序。你可以利用这个例行程序去通知客户机你的 input source 不再可用并且他们应该移除任何自己的相关的引用。表3-6 展示了取消者例行回调程序通过 RunLoopSource 对象进行注册。这个函数发送另一个 RunLoopContext 对象给 application 代理。但是这让代理去移除 runloop surce 的相关引用。</p>
<p>表3-6 销毁一个 input source</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> RunLoopSourceCancelRoutine (<span class="keyword">void</span> *info, <span class="built_in">CFRunLoopRef</span> rl, <span class="built_in">CFStringRef</span> mode)</span><br><span class="line">&#123;</span><br><span class="line">    RunLoopSource* obj = (RunLoopSource*)info;</span><br><span class="line">    AppDelegate* del = [AppDelegate sharedAppDelegate];</span><br><span class="line">    RunLoopContext* theContext = [[RunLoopContext alloc] initWithSource:obj andLoop:rl];</span><br><span class="line"> </span><br><span class="line">    [del performSelectorOnMainThread:<span class="keyword">@selector</span>(removeSource:)</span><br><span class="line">                                withObject:theContext waitUntilDone:<span class="literal">YES</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>笔记：应用代理方法  registerSource: 和 removeSource 方法在下面的章节 《协调 input source 的客户机》展示</code></p>
<p>###为 runloop 安装 input source<br>表3-7 展示了 <code>RunLoopSource</code> 类的 <code>init</code> 方法 和 <code>addToCurrentRunLoop</code> 方法。<code>init</code> 方法创建了 <a href="https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFRunLoopSourceRef/index.html#//apple_ref/c/tdef/CFRunLoopSourceRef" target="_blank" rel="noopener">CFRunLoopSource</a> 不透明类型的必须关联到 runloop 的对象。它会传递 <code>RunLoopSource</code> 对象自己作为 山下文信息 以便于例行回调程序有一个指向对象的指针。input source 直到线程唤起 <code>addToCurrentRunLoop</code> 方法时才会执行安装，准确将在 RunLoopSourceScheduleRoutine 回调函数调用时。 一旦 input source 安装到 runloop 中，线程将会运行自己的 runloop 去等待 input source 发出事件。</p>
<p>表3-7 安装 run loop source</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">id</span>)init &#123;</span><br><span class="line">    <span class="comment">// 创建上下文容器，其中会连接自己的 info，retain info release info，还会关联三个例行程序。</span></span><br><span class="line">    <span class="built_in">CFRunLoopSourceContext</span> context = &#123;<span class="number">0</span>, (__bridge <span class="keyword">void</span> *)(<span class="keyword">self</span>), <span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span> ,<span class="literal">NULL</span>, <span class="literal">NULL</span>, &amp;RunLoopSourceScheduleRoutine, RunLoopSourceCancelRoutine, RunLoopSourcePerformRoutine&#125;;</span><br><span class="line">    <span class="comment">/** 通过索引，上下文，和CFAllocator创建source */</span></span><br><span class="line">    runLoopSource = <span class="built_in">CFRunLoopSourceCreate</span>(<span class="literal">NULL</span>, <span class="number">0</span>, &amp;context);</span><br><span class="line">    commands = [[<span class="built_in">NSMutableArray</span> alloc] init];</span><br><span class="line">    <span class="keyword">return</span>  <span class="keyword">self</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)addToCurrentRunLoop&#123;</span><br><span class="line">    <span class="built_in">CFRunLoopRef</span> runLoop = <span class="built_in">CFRunLoopGetCurrent</span>();</span><br><span class="line">    <span class="built_in">CFRunLoopAddSource</span>(runLoop, runLoopSource, kCFRunLoopDefaultMode);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>###协调 input source 的客户机</p>
<p>对于你的 input source 会非常有用，你需要操作它并且从其他线程向它提供消息。input source 的要点是将其添加到线程并睡眠直到有事情要做时才唤醒。事实上很有必要让其他线程了解 input surce 并且有方法可以和它交流（沟通数据）。</p>
<p>通知你的 input source 客户机的方法之一是发出注册请求 当你的 input source 第一次安装到你的 runloop 中时。你可以向你的 input source 注册尽可能多的客户机。或者你仅仅只是简单的用一些中央机构，然后将你的 input source 声明为感兴趣的客户端进行注册。表3-8 展示了  通过代理 和 调用唤起定义的 注册方法 当 RunLoopSource 对象的调度者函数被调用时。这个方法将会收到 RunLoopSource 提供的 RunLoopContext 对象并且将它添加到他的 source 列表中。这个表也会展示 当 input source 从 他的 runloop 中被移除时 用来注销的例行程序。<br>表 3-8 使用 application 的 代理 注销并且移除 input source</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"> <span class="meta">#import <span class="meta-string">"YCRunLoopSource.h"</span></span></span><br><span class="line"> <span class="meta">#import <span class="meta-string">"YCRunLoopContext.h"</span></span></span><br><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">AppDelegate</span> : <span class="title">NSObject</span></span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">strong</span>) <span class="built_in">NSMutableArray</span> *sourcesToPing;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** 应该是一个单例 */</span></span><br><span class="line">+ (<span class="keyword">instancetype</span>)sharedAppDelegate;</span><br><span class="line">- (<span class="keyword">void</span>)registerSource:(YCRunLoopContext *)context;</span><br><span class="line">- (<span class="keyword">void</span>)removeSource:(YCRunLoopContext *)context;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> AppDelegate *_instance;</span><br><span class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">AppDelegate</span></span></span><br><span class="line"></span><br><span class="line">+ (<span class="keyword">instancetype</span>)sharedAppDelegate</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="built_in">dispatch_once_t</span> onceToken;</span><br><span class="line">    <span class="built_in">dispatch_once</span>(&amp;onceToken, ^&#123;</span><br><span class="line">        _instance = [[<span class="keyword">self</span> alloc] init];</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> _instance;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)registerSource:(YCRunLoopContext *)context</span><br><span class="line">&#123;</span><br><span class="line">    [<span class="keyword">self</span>.sourcesToPing addObject:context];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)removeSource:(YCRunLoopContext *)context</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">id</span> objToRemove = <span class="literal">nil</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (YCRunLoopContext *contextObj <span class="keyword">in</span> <span class="keyword">self</span>.sourcesToPing) &#123;</span><br><span class="line">        <span class="keyword">if</span> ([contextObj isEqual:context]) &#123;</span><br><span class="line">            objToRemove = contextObj;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (objToRemove) &#123;</span><br><span class="line">        [<span class="keyword">self</span>.sourcesToPing removeObject:objToRemove];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="built_in">NSMutableArray</span> *)sourcesToPing &#123;</span><br><span class="line">    <span class="keyword">if</span> (_sourcesToPing == <span class="literal">nil</span>) &#123;</span><br><span class="line">        _sourcesToPing = @[].mutableCopy;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> _sourcesToPing;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">@end</span></span><br></pre></td></tr></table></figure>

<p><strong>未完待续..</strong></p>
<h3 id="配置一个基于-port-的-input-source"><a href="#配置一个基于-port-的-input-source" class="headerlink" title="配置一个基于 port 的 input source"></a>配置一个基于 port 的 input source</h3><p>Cocoa 和 Core Foundation 都支持用于和线程间或者进程间通信的基于 端口的对象。接下来的章节将会向你展示如何使用一些不同类型的 port 构建 port 通信。</p>
<h3 id="配置一个NSMachPort-Object"><a href="#配置一个NSMachPort-Object" class="headerlink" title="配置一个NSMachPort Object"></a>配置一个NSMachPort Object</h3><p>使用 <a href="https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSMachPort_Class/index.html#//apple_ref/occ/cl/NSMachPort" target="_blank" rel="noopener">NSMachPort</a> 对象创建一个本地连接。你创建一个 port 对象并把它添加到你的主线程 run loop 中去。当启动你的子线程时，你要传一些相同的对象到你的线程入口点函数中去。子线程可以使用相同的对象发送信息回到你的主线程中去。</p>
<h3 id="实现主线程代码"><a href="#实现主线程代码" class="headerlink" title="实现主线程代码"></a>实现主线程代码</h3><p><a href="#list3-12">表 3-12</a> 中展示了用于启动子工作线程的主线程代码。因为 Cocoa 框架执行很多介入步骤用于配置 port 和 run loop ，Cocoa 的 <code>launchThread</code> 方法相比于 Core Foundation 的等价功能<a href="#list3-17">表 3-17</a>更加简洁明了。尽管如此，这两个框架在这一模块的功能表现基本都是相同的。其中一个存在的差异是与发送本地 port 到工作线程的方式不同，这个方法是直接发送 NSPort 对象的。</p>
<p><strong>表 3-12</strong> <span id="list3-12">Main Thread lauch method</span></p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)launchThread &#123;</span><br><span class="line">    <span class="built_in">NSPort</span> *myPort = [<span class="built_in">NSMachPort</span> port];</span><br><span class="line">    <span class="keyword">if</span> (myPort) &#123;</span><br><span class="line">        <span class="comment">// 这个类处理即将过来的 port 信息</span></span><br><span class="line">        [myPort setDelegate:<span class="keyword">self</span>];</span><br><span class="line">        <span class="comment">// 将此端口作为 input source 安装到当前 run loop 中去</span></span><br><span class="line">        [[<span class="built_in">NSRunLoop</span> currentRunLoop] addPort:myPort forMode:<span class="built_in">NSDefaultRunLoopMode</span>];</span><br><span class="line">        <span class="comment">// 开启工作子线程，让工作子线程去释放 port</span></span><br><span class="line">        [<span class="built_in">NSThread</span> detachNewThreadSelector:<span class="keyword">@selector</span>(LaunchThreadWithPort:) toTarget:[MyWorkerClass <span class="keyword">class</span>] withObject:myPort];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>为了设置为线程间双向通信信<br>道，在<br>登记信息中，你需要让工作线程发送自己的本地 port 到主线程。接收登记信息是为了让你的主线程知道开动子线程的过程进行的非常顺利，同时也为我们为提供了一种方法去向该线程发送更多信息。</p>
<p>表 3-13 展示了用于主线程的<a href="https://developer.apple.com/library/ios/documentation/Cocoa/Reference/NSPortDelegate_Protocol/index.html#//apple_ref/occ/intfm/NSPortDelegate/handlePortMessage:" target="_blank" rel="noopener">handlePortMessage:</a>方法，这个方法会在线程到达自己的本地 port 时进行调用。当登记信息到达时，该方法将直接从 port 信息中检索子线程的 port 并保存以备后用。</p>
<p><strong>表 3-13</strong>    <span id = "listing 3-13">处理 Mach port 信息</span></p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta"># <span class="meta-keyword">define</span> kCheckinMessage 100</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 处理工作线程的响应的代理方法</span></span><br><span class="line">- (<span class="keyword">void</span>)handlePortMessage:(<span class="built_in">NSPortMessage</span> *)portMessage</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">int</span> message = [portMessage msgid];</span><br><span class="line">    <span class="comment">// 定义远程端口</span></span><br><span class="line">    <span class="built_in">NSPort</span> *distantPort = <span class="literal">nil</span>;</span><br><span class="line">    <span class="keyword">if</span> (message == kCheckinMessage) &#123;</span><br><span class="line">        <span class="comment">// 获取工作线程的通信 port</span></span><br><span class="line">        distantPort = [portMessage sendPort];</span><br><span class="line">        </span><br><span class="line"><span class="comment">// 引用计数+1 并 保存工作端口以备后用</span></span><br><span class="line">        [<span class="keyword">self</span> storeDistantPort:distantPort];</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 处理其他信息</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)storeDistantPort:(<span class="built_in">NSPort</span> *)port &#123;</span><br><span class="line">    <span class="comment">// 保存远程端口</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="实现子线程代码"><a href="#实现子线程代码" class="headerlink" title="实现子线程代码"></a>实现子线程代码</h4><p>对于工作子线程，你必须配置它并且是使用指定的端口进行信息沟通并返回到主线程。</p>
<p>表 3-14 <span id = "liting3-14"> 使用 Mach port 启动子线程 </span></p>
<p><strong>未完待续。。</strong></p>

    </div>

    
    
    
        

  <div class="followme">
    <p>欢迎关注我的其它发布渠道</p>

    <div class="social-list">

        <div class="social-item">
          <a target="_blank" class="social-link" href="https://www.jianshu.com/u/f37a8f0ba6f8">
            <span class="icon">
              <i class="fas fa-book"></i>
            </span>

            <span class="label">简书</span>
          </a>
        </div>
    </div>
  </div>


      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2016/03/04/%E8%AE%A9-iOS-Android-%E7%BD%91%E7%BB%9CAPI%E5%BC%80%E5%8F%91%E6%9B%B4%E5%8A%A0%E8%87%AA%E7%94%B1-charles%E6%A8%A1%E6%8B%9F%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE/" rel="prev" title="让-iOS-Android-网络API开发更加自由-charles模拟服务器返回数据">
      <i class="fa fa-chevron-left"></i> 让-iOS-Android-网络API开发更加自由-charles模拟服务器返回数据
    </a></div>
      <div class="post-nav-item">
    <a href="/2017/03/03/%E5%88%A9%E7%94%A8-RACObserve-%E5%8F%82%E6%95%B0%E4%BA%8C%E6%8E%A7%E5%88%B6%E7%9B%91%E5%90%AC%E8%8C%83%E5%9B%B4/" rel="next" title="利用-RACObserve-参数二控制监听范围">
      利用-RACObserve-参数二控制监听范围 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          
    <div class="comments" id="valine-comments"></div>

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#使用-Run-Loop-对象"><span class="nav-number">1.</span> <span class="nav-text">使用 Run Loop 对象</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#获取-Run-Loop-对象"><span class="nav-number">2.</span> <span class="nav-text">获取 Run Loop 对象</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#配置-Run-Loop"><span class="nav-number">3.</span> <span class="nav-text">配置 Run Loop</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#开动-run-loop"><span class="nav-number">4.</span> <span class="nav-text">开动 run loop</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#退出-RunLoop"><span class="nav-number">4.1.</span> <span class="nav-text">退出 RunLoop</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#线程安全-和-Run-Loop-对象"><span class="nav-number">4.2.</span> <span class="nav-text">线程安全 和 Run Loop 对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#配置-run-loop-source"><span class="nav-number">4.3.</span> <span class="nav-text">配置 run loop source</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#定义一个自定义自定义-input-source"><span class="nav-number">4.3.1.</span> <span class="nav-text">定义一个自定义自定义 input source</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#定义-input-source"><span class="nav-number">4.4.</span> <span class="nav-text">定义 input source</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#配置一个基于-port-的-input-source"><span class="nav-number">4.5.</span> <span class="nav-text">配置一个基于 port 的 input source</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#配置一个NSMachPort-Object"><span class="nav-number">4.6.</span> <span class="nav-text">配置一个NSMachPort Object</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#实现主线程代码"><span class="nav-number">4.7.</span> <span class="nav-text">实现主线程代码</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#实现子线程代码"><span class="nav-number">4.7.1.</span> <span class="nav-text">实现子线程代码</span></a></li></ol></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="SuperYang"
      src="https://tva2.sinaimg.cn/crop.259.17.290.290.180/c2abdfa9jw8ez7appr3p2j20g40a2t91.jpg?KID=imgbed,tva&Expires=1594186820&ssig=BdxhDdi1Ti">
  <p class="site-author-name" itemprop="name">SuperYang</p>
  <div class="site-description" itemprop="description">牛肉炒饭不要香菜，老板收钱</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">39</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">5</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">31</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/yangchao0033" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;yangchao0033" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:ygrfwyc@gmail.com" title="E-Mail → mailto:ygrfwyc@gmail.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://weibo.com/yangchao0033" title="Weibo → https:&#x2F;&#x2F;weibo.com&#x2F;yangchao0033" rel="noopener" target="_blank"><i class="fab fa-weibo fa-fw"></i>Weibo</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://www.jianshu.com/u/f37a8f0ba6f8" title="简书 → https:&#x2F;&#x2F;www.jianshu.com&#x2F;u&#x2F;f37a8f0ba6f8" rel="noopener" target="_blank"><i class="fas fa-book fa-fw"></i>简书</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://t.me/superYang0033" title="Telegram → https:&#x2F;&#x2F;t.me&#x2F;superYang0033" rel="noopener" target="_blank"><i class="fab fa-telegram fa-fw"></i>Telegram</a>
      </span>
  </div>



      </div>

      
        <div id="music163player">
        <iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=350 height=86 src="//music.163.com/outchain/player?type=2&id=31473269&auto=0&height=66">
        </iframe>
        </div>
    </div>

    

  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SuperYang</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-area"></i>
    </span>
    <span title="站点总字数">157k</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">2:23</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  
  <script>
    (function(){
      var canonicalURL, curProtocol;
      //Get the <link> tag
      var x=document.getElementsByTagName("link");
		//Find the last canonical URL
		if(x.length > 0){
			for (i=0;i<x.length;i++){
				if(x[i].rel.toLowerCase() == 'canonical' && x[i].href){
					canonicalURL=x[i].href;
				}
			}
		}
    //Get protocol
	    if (!canonicalURL){
	    	curProtocol = window.location.protocol.split(':')[0];
	    }
	    else{
	    	curProtocol = canonicalURL.split(':')[0];
	    }
      //Get current URL if the canonical URL does not exist
	    if (!canonicalURL) canonicalURL = window.location.href;
	    //Assign script content. Replace current URL with the canonical URL
      !function(){var e=/([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,r=canonicalURL,t=document.referrer;if(!e.test(r)){var n=(String(curProtocol).toLowerCase() === 'https')?"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif":"//api.share.baidu.com/s.gif";t?(n+="?r="+encodeURIComponent(document.referrer),r&&(n+="&l="+r)):r&&(n+="?l="+r);var i=new Image;i.src=n}}(window);})();
  </script>




  
<script src="/js/local-search.js"></script>













  

  

  

  <script async src="/js/cursor/fireworks.js"></script>



<script>
NexT.utils.loadComments(document.querySelector('#valine-comments'), () => {
  NexT.utils.getScript('//unpkg.com/valine/dist/Valine.min.js', () => {
    var GUEST = ['nick', 'mail', 'link'];
    var guest = 'nick,mail,link';
    guest = guest.split(',').filter(item => {
      return GUEST.includes(item);
    });
    new Valine({
      el         : '#valine-comments',
      verify     : true,
      notify     : true,
      appId      : 'zizRqalUJY55Xc5oBJKbhxpV-gzGzoHsz',
      appKey     : '64gPcVi8lIA8zVGpBSEk7uKu',
      placeholder: "留言板无需注册登录，快来评论吧。。",
      avatar     : 'mm',
      meta       : guest,
      pageSize   : '10' || 10,
      visitor    : true,
      lang       : '' || 'zh-cn',
      path       : location.pathname,
      recordIP   : false,
      serverURLs : ''
    });
  }, window.Valine);
});
</script>

</body>
</html>
